package com.redstar.interesting.common.starter.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @ClassName RedisLockImpl
 * @Author cwx
 * @Date 2021/11/25 17:12
 **/
@Component
public class RedisLockImpl implements RedisLock {

    private Logger log = LoggerFactory.getLogger(RedisLockImpl.class);
    /**
     * 过期时间
     */
    private static final int DEFAULT_SINGLE_EXPIRE_TIME = 5;
    /**
     * redis客户端接口封装类
     */
    @Autowired
    private RedisService redisService;

    public void setRedisService(RedisService redisService) {
        this.redisService = redisService;
    }

    /**
     * 获取锁 如果锁可用   立即返回true，  否则立即返回false，作为非阻塞式锁使用
     */
    @Override
    public boolean tryLock(String key, String value) {
        return tryLock(key, value, 0L, null);
    }

    /**
     * 锁在给定的等待时间内空闲，则获取锁成功 返回true， 否则返回false，作为阻塞式锁使用
     *
     * @param key 锁键
     * @param value 被谁锁定
     * @param timeout 尝试获取锁时长，建议传递500,结合实践单位，则可表示500毫秒
     * @param unit 建议传递TimeUnit.MILLISECONDS
     */
    @Override
    public boolean tryLock(String key, String value, long timeout, TimeUnit unit) {
        return tryLock(key, value, timeout, unit, DEFAULT_SINGLE_EXPIRE_TIME);
    }

    /**
     * 锁在给定的等待时间内空闲，则获取锁成功 返回true， 否则返回false，作为阻塞式锁使用
     *
     * @param key 锁键
     * @param value 被谁锁定
     * @param timeout 尝试获取锁时长，建议传递500,结合实践单位，则可表示500毫秒
     * @param unit 建议传递TimeUnit.MILLISECONDS
     * @param expire 锁的过期时间单位:秒
     */
    @Override
    public boolean tryLock(String key, String value, long timeout, TimeUnit unit, long expire) {
        // 纳秒
        long begin = System.nanoTime();
        try {
            do {
                expire = expire > 0 ? expire : DEFAULT_SINGLE_EXPIRE_TIME;
                boolean nx = redisService.setnx(getLockKey(key), value, expire);
                if (nx) {
                    log.debug("{}成功获取{}的锁,设置锁过期时间为{}秒 ", value, key, expire);
                    return true;
                }
                if (timeout == 0) {
                    break;
                }
                //在其睡眠的期间，锁可能被解，也可能又被他人占用，但会尝试继续获取锁直到指定的时间
                Thread.sleep(100);
            } while ((System.nanoTime() - begin) < unit.toNanos(timeout));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        // 因超时没有获得锁
        return false;
    }

    /**
     * 释放单个锁,只能由持有锁的线程释放锁
     *
     * @param key 锁键
     */
    @Override
    public void unLock(String key, String value) {
        try {
            String hold = redisService.getString(getLockKey(key));
            if (value.equals(hold)) {
                redisService.delete(getLockKey(key));
            } else {
                log.info("{}锁非持有者不能释放,value:{},hold:{} .", key, value, hold);
            }
            log.debug("{}锁被释放 .", key);
        } catch (Exception e) {
            log.error("释放锁异常", e);
        }
    }

    /**
     * 获得Redis锁的key
     *
     * @param key - 要锁的key
     * @return key
     */
    @Override
    public String getLockKey(String key) {
        return ("R_lock_" + key);
    }
}
